﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace MIPS_Simulator
{
    class Control
    {
        private ProgramCounter pc;
        public ProgramCounter PC { get { return pc; } set { pc = value; } }

        private ALU alu;
        public ALU Alu { get { return alu; } set { alu = value; } }

        private Memory memory;
        public Memory Memory { get { return memory; } set { memory = value; } }

        private Registers registers;
        public Registers Registers { get { return registers; } set { registers = value; } }
        private bool regDest;
        public bool RegDest
        {
            get
            {
                return regDest;
            }
            set
            {
                regDest = value;
            }
        }
        private bool jump;
        public bool Jump
        {
            get
            {
                return jump;
            }
            set
            {
                jump = value;
            }
        }
        private bool branch;
        public bool Branch
        {
            get
            {
                return branch;
            }
            set
            {
                branch = value;
            }
        }
        private bool memRead;
        public bool MemRead
        {
            get
            {
                return MemRead;
            }
            set
            {
                MemRead = value;
            }
        }
        private bool memToReg;
        public bool MemToReg
        {
            get
            {
                return memToReg;
            }
            set
            {
                memToReg = value;
            }
        }
        private bool memWrite;
        public bool MemWrite
        {
            get
            {
                return memWrite;
            }
            set
            {
                memWrite = value;
            }
        }
        private bool aluSrc;
        public bool ALUSrc
        {
            get
            {
                return aluSrc;
            }
            set
            {
                aluSrc = value;
            }
        }
        private bool regWrite;
        public bool RegWrite
        {
            get
            {
                return regWrite;
            }
            set
            {
                regWrite = value;
            }
        }

        private int aluOp;
        public int ALUOp{
            get
            {
                return aluOp;
            }

            set
            {
                aluOp = value;
            }
        }

        private bool jal;
        public bool JAL
        {
            get
            {
                return jal;
            }
            set
            {
                jal = value;
            }
        }

        private bool jr;
        public bool JR
        {
            get
            {
                return jr;
            }
            set
            {
                regWrite = jr;
            }
        }

        private int shiftAmount;
        public int ShiftAmount
        {
            get
            {
                return shiftAmount;
            }
            set
            {
                shiftAmount = value;
            }
        }

        private bool shiftLeft;
        public bool ShiftLeft
        {
            get
            {
                return shiftLeft;
            }
            set
            {
                shiftLeft = value;
            }
        }

        private bool shiftRight;
        public bool ShiftRight
        {
            get
            {
                return shiftRight;
            }
            set
            {
                shiftRight = value;
            }
        }

        private bool branchNE;
        public bool BranchNE
        {
            get
            {
                return branchNE;
            }
            set
            {
                branchNE = value;
            }
        }

        private bool slt;
        public bool SLT
        {
            set
            {
                slt = value;
            }
            get
            {
                return slt;
            }
        }

        private bool slti;

        private bool sltu;

        private bool sltui;

        private bool lw;

        private bool sw;

        private const short returnAddress = 23910;

        public Control(ProgramCounter pc, Registers r, Memory m, ALU a)
        {
            PC = pc;
            Registers = r;
            Memory = m;
            Alu = a;
        }

        public void Compile(List<string> OPCODE)
        {
            controller((string)OPCODE[0]);
            switch ((string)OPCODE[0])
            {
                case "add":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = registers.loadReg(short.Parse(OPCODE[3]));
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;
                case "addi":
                    alu.Read1 = registers.loadReg(short.Parse((string)OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = short.Parse((string)OPCODE[3]);
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse((string)OPCODE[1])); break;
                case "sub":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = registers.loadReg(short.Parse(OPCODE[3]));
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;
                case "and":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = registers.loadReg(short.Parse(OPCODE[3]));
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;
                case "andi":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = short.Parse(OPCODE[3]);
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;
                case "or":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = registers.loadReg(short.Parse(OPCODE[3]));
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;
                case "ori":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = short.Parse(OPCODE[3]);
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;
                case "nor":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = registers.loadReg(short.Parse(OPCODE[3]));
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;
                case "sll":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = registers.loadReg(short.Parse(OPCODE[3]));
                    Wires.ALURead2 = alu.Read2;
                    alu.Calculate();
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;
                case "slr":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = registers.loadReg(short.Parse(OPCODE[3]));
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;

                case "slt":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = registers.loadReg(short.Parse(OPCODE[3]));
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;

                case "slti":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = short.Parse(OPCODE[3]);
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;
                case "sltu":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = registers.loadReg(short.Parse(OPCODE[3]));
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;
                case "sltui":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[2]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = short.Parse(OPCODE[3]);
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.ALUResult = alu.Result;
                    registers.StoreReg(alu.Result, short.Parse(OPCODE[1])); break;
                case "j":
                    break;
                case "beq":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[1]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = registers.loadReg(short.Parse(OPCODE[2]));
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.Zero = alu.Zero ? 1 : 0;
                    break;
                case "bne":
                    alu.Read1 = registers.loadReg(short.Parse(OPCODE[1]));
                    Wires.ALURead1 = alu.Read1;
                    alu.Read2 = registers.loadReg(short.Parse(OPCODE[2]));
                    alu.Calculate();
                    Wires.ALURead2 = alu.Read2;
                    Wires.Zero = alu.Zero ? 1 : 0;
                    break;
                case "jal":
                    registers.StoreReg((long)pc.NextInstruction, returnAddress);break;
                case "jr":
                    pc.NextInstruction = ((short)registers.loadReg(returnAddress));break;
                case "lw":
                    int addr = (short.Parse(OPCODE[3]));
                    short addreg = short.Parse(OPCODE[2]);
                    registers.StoreReg(memory.loadWord(registers.loadReg(addreg) + addr), short.Parse(OPCODE[1]));
                    break;
                case "sw": 
                    int addre = (short.Parse(OPCODE[3])); memory.storeWord(registers.loadReg(short.Parse(OPCODE[1])), (short.Parse(OPCODE[2]) + addre)); break;
                default: throw new InvalidInstructionException();
            }

        }
        public void controller(string instruction)
        {
            switch (instruction)
            {
                case "add": registers.RegWrite = true; alu.Control = 3; aluSrc = false; regDest = true; 
                    Wires.Add(); break;

                case "addi": registers.RegWrite = true; alu.Control = 3; aluSrc = true; regDest = true; 
                    Wires.Add(); Wires.ALUSrc = 1; break;

                case "sub": registers.RegWrite = true; alu.Control = 4; aluSrc = false; regDest = true; 
                    Wires.Sub(); break;

                case "and": registers.RegWrite = true; alu.Control = 1; aluSrc = false; regDest = true; 
                    Wires.And(); break;

                case "andi": registers.RegWrite = true; alu.Control = 1; aluSrc = true; regDest = true; 
                    Wires.And(); Wires.ALUSrc = 1; break;

                case "or": registers.RegWrite = true; alu.Control = 2; aluSrc = false; regDest = true; 
                    Wires.Or(); break;

                case "ori": registers.RegWrite = true; alu.Control = 2; aluSrc = true; regDest = true; 
                    Wires.Or(); Wires.ALUSrc = 1; break;

                case "nor": registers.RegWrite = true; alu.Control = 7; aluSrc = false; regDest = true; 
                    Wires.RegWrite = 1; Wires.RegDst = 1; Wires.ALUOP = 10; Wires.ALUFunction = 100111; Wires.ALUControl = 1111; break;

                case "sll": registers.RegWrite = true; shiftLeft = true; regDest = true; alu.Control = 8;
                    Wires.RegDst = 1; Wires.RegWrite = 1; Wires.ALUOP = 10; Wires.ALUFunction = 101000; Wires.ALUControl = 1000; break;//not sure

                case "srl": registers.RegWrite = true; shiftRight = true; regDest = true; alu.Control = 9;
                    Wires.RegDst = 1; Wires.RegWrite = 1; Wires.ALUOP = 10; Wires.ALUFunction = 101000; Wires.ALUControl = 1001; break;//not sure

                case "beq": alu.Control = 10;  branch = true; 
                    Wires.BranchIfEqual = 1; Wires.ALUOP = 01; Wires.ALUControl = 0110; break;

                case "bne": alu.Control = 10;  branchNE = true; 
                    Wires.BranchIfNotEqual = 1; Wires.ALUOP = 01; Wires.ALUControl = 0110; break;

                case "j": jump = true; Wires.Jump = 1;  break;

                case "jal": registers.RegWrite = true; jal = true; Wires.RegWrite = 1;
                    Wires.JumpAndLink = 1; Wires.RegDestination = returnAddress; Wires.WriteData = pc.NextInstruction; break;//destination always ra

                case "jr": jr = true; regDest = true; Wires.RegDst = 1;  break;//

                case "slt": registers.RegWrite = true; slt = true; aluSrc = false; alu.Control = 5; regDest = true; 
                    Wires.RegDst = 1; Wires.RegWrite = 1; break;

                case "slti": registers.RegWrite = true; slti = true; aluSrc = true; alu.Control = 5; regDest = true; 
                    Wires.RegDst = 1; Wires.RegWrite = 1; break;

                case "sltu": registers.RegWrite = true; sltu = true; aluSrc = false; alu.Control = 6; regDest = true; 
                    Wires.RegDst = 1; Wires.RegWrite = 1; break;

                case "sltui": registers.RegWrite = true; sltui = true; aluSrc = true; alu.Control = 6; regDest = true;
                    Wires.RegDst = 1; Wires.RegWrite = 1;break;

                case "lw": memory.MemRead = true;  registers.RegWrite = true; lw = true; regDest = false; break;

                case "sw": memory.MemWrite = true; registers.RegWrite = true; sw = true; regDest = false; break;

                default: ; break;
            }
        }
        
    }
}
